package com.jiuli_travel.blog_service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiuli_travel.blog_service.mapper.BlogMapper;
import com.jiuli_travel.blog_service.model.cache.blog.BlogCollection;
import com.jiuli_travel.blog_service.model.cache.blog.BlogLiked;
import com.jiuli_travel.blog_service.model.dto.blog.BlogInsertRequest;
import com.jiuli_travel.blog_service.model.dto.blog.BlogQueryRequest;
import com.jiuli_travel.blog_service.model.dto.blog.HandleCountChangeRequest;
import com.jiuli_travel.blog_service.model.dto.blog_comment.BlogCommentQueryRequest;
import com.jiuli_travel.blog_service.model.entity.Blog;
import com.jiuli_travel.blog_service.model.vo.BlogCommentVO;
import com.jiuli_travel.blog_service.model.vo.BlogVO;
import com.jiuli_travel.blog_service.service.IBlogCommentService;
import com.jiuli_travel.blog_service.service.IBlogService;
import com.jiuli_travel.user_service.model.cache.UserCache;
import com.jiuli_travel.user_service.model.entity.User;
import com.jiuli_travel.user_service.model.vo.UserVO;
import com.jiuli_travel.user_service.service.IUserService;
import com.jiuli_travel.utils.PageDTO;
import com.jiuli_travel.utils.Result;
import com.jiuli_travel.utils.UserHolder;
import jakarta.servlet.http.HttpServletRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.redisson.api.RKeys;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.redisson.api.options.KeysOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.jiuli_travel.utils.RedisConstants.*;

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private HttpServletRequest httpServletRequest;
    
    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private BlogLiked blogLiked;

    @Autowired
    private BlogCollection blogCollection;

    @Autowired
    private IUserService userService;

    @Autowired
    private IBlogCommentService blogCommentsService;


    /**
     *  条件分页查询博客的信息
     */
    public Result selectBlogsByPage(BlogQueryRequest blogQueryRequest){
        // 获取当前登录用户
        UserVO loginUser = UserHolder.getUser();

        /**
         * 查询blog表的基础信息
         */
        // 构造分页条件
        Page<Blog> mp = blogQueryRequest.toMp();
        
        // 构造查询条件并查询
        lambdaQuery()
                .like(blogQueryRequest.getTitle() != null, Blog::getTitle, blogQueryRequest.getTitle())
                .page(mp);

        // 校验是否有blog数据
        if(mp.getTotal() == 0){
            return null;
        }

        /**
         * TODO 转换数据格式与封装各类信息
         */
        List<BlogVO> blogVOS = handelBlogListToBlogVOS(mp.getRecords(), loginUser);

        return Result.ok(blogVOS, (long) blogVOS.size());

    }

    /**
     * selectInitBlogCommentsByPage
     */
    private void selectInitBlogCommentsByPage(BlogVO blogVO) {
        //1.1 发起请求获取数据
//        String url = "http://localhost:8083/blogComments/selectInitBlogCommentsByPage";
//        Result result = restTemplate.getForObject(
//                url,
//                Result.class,
//                blogCommentQueryRequest
//        );
        PageDTO<BlogCommentVO> blogCommentVOPageDTO = blogCommentsService.selectInitBlogCommentsByPage(blogVO.getId());
        //1.2 封装用户信息
        if(blogCommentVOPageDTO == null){
            return;
        }
//        String jsonStr = JSONUtil.toJsonStr(result.getData());
        blogVO.setCommentsList(BeanUtil.copyProperties(blogCommentVOPageDTO, PageDTO.class));
    }

    /**
     * getUserByUserId
     */
    private void getUserByUserId(BlogVO blogVO) {
        //1.1 发起请求获取数据
//        String url = "http://localhost:8083/user/getUserByUserId?"+"userId="+ blogVO.getUserId();
//        Result result = restTemplate.getForObject(
//                url,
//                Result.class
//        );
        Result result = userService.getUserByUserId(blogVO.getUserId());
        //1.2 封装用户信息
        if(result == null){
            return;
        }
        UserVO userVO = BeanUtil.toBean(result.getData(), UserVO.class);   //object对象转一般类型 用beanUtil.toBean拷贝
        blogVO.setUserVO(userVO);
    }

    //发布个人 blog
    @Override
    public void publishBlog(BlogInsertRequest blogInsertRequest) {
        // 图片信息不能为空
        List<String> imageList = blogInsertRequest.getImageList();
        if(imageList.isEmpty()){
            return;
        }

        // 获取发布该博客的用户信息
        UserVO user = UserHolder.getUser();

        /**
         * 1. 对于 blog表 进行处理
         */
        Blog blog = BeanUtil.copyProperties(blogInsertRequest, Blog.class);
        blog.doListToJsonStr(imageList,
                                blogInsertRequest.getRecommendedTopicTagList(),
                                blogInsertRequest.getAiteUserList());

        // 2. 进行发布
        save(blog);

        /**
         * TODO 对其他的附属内容进行处理
         */

    }

    // 删除个人 blog
    @Override
    public void batchDelBlog(List<Long> rList) {
        /**
         * 对 blog 表进行处理
         */
        //构造删除条件
        LambdaQueryChainWrapper<Blog> delWrapper = lambdaQuery()
                .in(!rList.isEmpty(), Blog::getId, rList);
        boolean remove = remove(delWrapper);
        if(!remove) return;

        /**
         * TODO 对和 blog 有依赖关系的东西进行清理
         */

        rList.forEach(blogId -> {
            /**
             * TODO 与该博客有关的评论信息进行清空 （也可以进行后续异步处理）
             */
        });

    }

    // searchBlogs
    @Override
    public Result searchBlogs(String searchText, Integer from, Integer size) throws IOException {
        // 获取当前登录用户的信息
        UserVO loginUser = UserHolder.getUser();
        // 1. 准备请求
        SearchRequest blogEsRequest = new SearchRequest("blog_es");

        // 2. DSL
        SearchSourceBuilder query = blogEsRequest.source()
                .query(QueryBuilders.boolQuery()
                        .should(QueryBuilders
                                .matchQuery("title", searchText))
                        .should(QueryBuilders.matchQuery("content", searchText))
                        .filter(QueryBuilders.termQuery("isDeleted", 0))
                )
                .from(from).size(size)
                .fetchSource(new String[]{"id"}, null);

        // 3. 发起请求
        SearchResponse searchResponse = restHighLevelClient.search(blogEsRequest, RequestOptions.DEFAULT);

        // 4. 解析数据
        // 将rList存起来
        List<Long> rList = new ArrayList<>();
        // 将总数存起来
        long total = 0L;
        SearchHits searchResponseHits = searchResponse.getHits();
        total = searchResponseHits.getTotalHits().value;
        SearchHit[] hits = searchResponseHits.getHits();
        if(hits.length == 0){
            return Result.fail("空空如也");
        }
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            long blogId = Long.parseLong(sourceAsMap.get("id").toString());
            rList.add(blogId);
        }

        // 5. 查询数据库封装数据
        String rListStr = rList.stream().map(String::valueOf).collect(Collectors.joining(","));
        List<Blog> blogList = lambdaQuery()
                .in(Blog::getId, rList)
                .last("ORDER BY FIELD(ID, " + rListStr + ")")
                .list();
        if (blogList.isEmpty()){
            return Result.fail("空空如也");
        }

        //转换数据格式 and 处理封装数据
        List<BlogVO> blogVOS = handelBlogListToBlogVOS(blogList, loginUser);

        return Result.ok(blogVOS, total);

    }

    // liked（点赞）
    public void handelLikedCountChange(Long blogId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();

        // 2. 查询redis，看该用户是否有进行点赞
        RList<Long> rList = redissonClient.getList(BLOG_LIKED_KEY + blogId);

        // 3. 判断该用户是否有点赞
        Long loginUserId = loginUser.getId();
        boolean flag = rList.contains(loginUserId);
        if(flag){
            // 3.1 已经点赞（消除点赞, 更新数据库）
            rList.remove(loginUserId);
            lambdaUpdate()
                    .setSql("liked_count = liked_count - 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }else{
            // 3.2 还未点赞 （点赞，跟新数据库）
            rList.add(loginUserId);
            lambdaUpdate()
                    .setSql("liked_count = liked_count + 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }
        
    }
    
    // collect（收藏）
    public void handelCollectCountChange(Long blogId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();

        // 2. 查询redis，看该用户是否有进行收藏
        RList<Long> rList = redissonClient.getList(BLOG_COLLECT_KEY + blogId);

        // 3. 判断该用户是否有收藏
        Long loginUserId = loginUser.getId();
        boolean flag = rList.contains(loginUserId);
        if(flag){
            // 3.1 已经收藏（消除收藏, 更新数据库）
            rList.remove(loginUserId);
            lambdaUpdate()
                    .setSql("collect_count = collect_count - 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }else{
            // 3.2 还未收藏 （收藏，跟新数据库）
            rList.add(loginUserId);
            lambdaUpdate()
                    .setSql("collect_count = collect_count + 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }
    }
    
    // view（浏览）
    public void handelViewCountChange(Long blogId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();

        // 2. 查询redis，看该用户是否有进行浏览
        RList<Long> rList = redissonClient.getList(BLOG_VIEW_KEY + blogId);

        // 3. 判断该用户是否有浏览
        Long loginUserId = loginUser.getId();
        boolean flag = rList.contains(loginUserId);
        if(flag){
            // 3.1 已经浏览（消除浏览, 更新数据库）
            rList.remove(loginUserId);
            lambdaUpdate()
                    .setSql("view_count = view_count - 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }else{
            // 3.2 还未浏览 （浏览，跟新数据库）
            rList.add(loginUserId);
            lambdaUpdate()
                    .setSql("view_count = view_count + 1")
                    .eq(Blog::getId, blogId)
                    .update();
        }
    }

    // 考虑一个点就是是否需要分页，这里应该不用，因为分页本身就是消耗性能的，而这类数据量较小 TODO 明天再写
    /**
     * 获取用户publish的blog
     */
    public Result getPublishBlogsByUserId(Long userId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();
        
        // 2. 获取该用户自己发布的blog
        List<Blog> blogList = lambdaQuery()
                .eq(Blog::getUserId, userId)
                .list();
        if (blogList == null){
            return Result.fail("空空如也");
        }

        // 3. 转化数据格式与封装给类数据
        List<BlogVO> blogVOS = handelBlogListToBlogVOS(blogList, loginUser);
        return Result.ok(blogVOS, (long) blogVOS.size());
    }

    /**
     * 获取用户收藏的blog
     */
    public Result getCollectBlogsByUserId(Long userId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();

        // 2. 获取该用户自己收藏的blogIds
        List<Blog> blogList = getUserBlogsByType(userId, BLOG_COLLECT_KEY);
        if (blogList == null){
            return Result.fail("空空如也");
        }

        // 3. 转化数据格式与封装给类数据
        List<BlogVO> blogVOS = handelBlogListToBlogVOS(blogList, loginUser);
        return Result.ok(blogVOS, (long) blogVOS.size());
    }

    // 获取 liked | view | collect | shared 的blogs
    private List<Blog> getUserBlogsByType(Long userId, String typeKey) {
        // 获取 liked | view | collect | shared blogIds
        List<Long> blogIds = new ArrayList<>();
        RKeys rKeys = redissonClient.getKeys();
        Iterable<String> rKeysKeysByPattern = rKeys.getKeysByPattern(typeKey + "*");
        for (String s : rKeysKeysByPattern) {
            RList<Long> rList = redissonClient.getList(s);
            if(rList.contains(userId)){
                blogIds.add(Long.valueOf(s.split(":")[2]));
            }
        }
        if(blogIds.isEmpty()){
            return null;
        }
        // 根据blogIds获取blogs
        List<Blog> blogList = lambdaQuery()
                .in(Blog::getId, blogIds)
                .list();
        return blogList;
    }

    /**
     * 获取用户赞过的blog
     */
    public Result getLikedBlogsByUserId(Long userId){
        // 1. 获取当前登录的用户信息
        UserVO loginUser = UserHolder.getUser();

        // 2. 获取该用户自己收藏的blogIds
        List<Blog> blogList = getUserBlogsByType(userId, BLOG_LIKED_KEY);
        if (blogList == null){
            return Result.fail("空空如也");
        }
        // 3. 转化数据格式与封装给类数据
        List<BlogVO> blogVOS = handelBlogListToBlogVOS(blogList, loginUser);
        return Result.ok(blogVOS, (long) blogVOS.size());
    }

    /**
     *  处理博客原始列表 （转换数据格式， 封装各类信息）
     * @param blogList 原始博客列表
     * @param loginUser 登录的用户信息
     * @return
     */
    private List<BlogVO> handelBlogListToBlogVOS(List<Blog> blogList, UserVO loginUser) {
        // 3. 转换数据格式
        List<BlogVO> blogVOS = blogList.stream().map(
                new Function<Blog, BlogVO>() {
                    @Override
                    public BlogVO apply(Blog blog) {
                        BlogVO blogVO = BeanUtil.copyProperties(blog, BlogVO.class);
                        blogVO.doJsonStrToList(blog.getImages(), blog.getRecommendedTopicTags(), blog.getAiteUsers());
                        return blogVO;
                    }
                }
        ).collect(Collectors.toList());

        /**
         * TODO 封装各类信息
         */

        blogVOS
                .forEach(blogVO -> {
                    /**
                     * 1. 每篇博客都会有发布者 （封装发布者的信息） userVO的封装
                     */
                    getUserByUserId(blogVO);

                    /**
                     * 2. 初始化评论区的信息
                     */
                    selectInitBlogCommentsByPage(blogVO);

                    /**
                     * 3. 当前登录用户 对 liked | collect
                     */
                    // 3.1 liked
                    RList<Long> rLikedList = redissonClient.getList(BLOG_LIKED_KEY + blogVO.getId());
                    if(rLikedList.contains(loginUser.getId())){
                        blogVO.setLikedFlag(true);
                    }
                    // 3.2 collect
                    RList<Long> rCollectList = redissonClient.getList(BLOG_COLLECT_KEY + blogVO.getId());
                    if(rCollectList.contains(loginUser.getId())){
                        blogVO.setCollectFlag(true);
                    }
                });
        return blogVOS;
    }

}






















